%!
% bb.ps --
%
% Prints a file, but keeps track of bounding box info, and prints the box at
% the end (around the figure.)
%
% $Header: bb.ps,v 1.14 91/03/21 13:04:02 cosell Exp $
% RCS log info at end

50 dict /$BoundingBox exch def

$BoundingBox begin

/xdef {
	exch def
} def

/xstore {
	exch store
} def

/addcoords {
	exch
	4 -1 roll add
	3 1 roll add
} def

%
% Stubs of old functions.
%

/-stroke /stroke load def
/-fill /fill load def
/-eofill /eofill load def
/-image /image load def
/-show /show load def
/-awidthshow /awidthshow load def
/-showpage /showpage load def
/-restore  /restore load def
/-imagemask /imagemask load def

end % $BoundingBox

%
% New Functions.   --- These go into the user dict to intercept the calls
%

/stroke {
        $BoundingBox begin
	gsave
	initmatrix
	(stroke called\n) traceprint %%DEBUG
	{
	 	strokepath	% Make sure to take line width into account.
		0 setlinejoin
		flattenpath
	} stopped {		% strokepath often hits a limitcheck.
		(Can't set up a strokepath\n) traceprint % DEBUG
		grestore	% Restore the original path
		gsave
	} if
	includepath			% Accumulate it into our box.
	grestore

	-stroke
	end % $BoundingBox
} def

/fill {
        $BoundingBox begin
	gsave
	(fill called\n) traceprint %%DEBUG
	includepath
	grestore

	-fill
	end % $BoundingBox
} def

/eofill {
        $BoundingBox begin
	gsave
	(eofill called\n) traceprint %%DEBUG
	includepath
	grestore

	-eofill
	end % $BoundingBox
} def

%
% Text is implemented by reducing everything to an `awidthshow'.
%

/show {
        $BoundingBox begin
	(show called\n) traceprint %%DEBUG
	0 0 0 0 0		% Extra parameters for awidthshow
	6 -1 roll		% Bring the string back up
	awidthshow
	end % $BoundingBox
} def

/widthshow {
        $BoundingBox begin
        (widthshow called\n) traceprint %%DEBUG
	0 0			% Extra parameters for awidthshow
	3 -1 roll		% Bring the string back up.
	awidthshow
	end % $BoundingBox
} def

/ashow {
        $BoundingBox begin
        (ashow called\n) traceprint %%DEBUG
	0 0 0
	6 3 roll
	awidthshow
	end % $BoundingBox
} def


% This does all of the work of the text-rendering operators
%   What it does, is compute, basically brute force, what 'charpath'
%   would have given us virtually for free, if 'show' were the only
%   operator that we needed to do.

/awidthshow {
	$BoundingBox begin
	gsave
	6 (awidthshow:) debug %%DEBUG
	currentpoint
	2 copy /@starty xdef /@startx xdef
	2 index stringwidth	% Get the natural length of the string
	addcoords			% Add to the start to get the end.

	2 index length		% How many characters?

	dup			% Add the offsets to each character
	6 index mul
	exch 5 index mul
	addcoords

	5 index 3 index
	chcount		% How many padding characters?

	dup			% Add the offsets for each pad.
	9 index mul
	exch 8 index mul
	addcoords

	/@endy xdef /@endx xdef

	% We now have the left and right edges (in user coords)
	% of the text.  Now we need only correct for the vertical
	% displacements needed for the font and we can get the
	% top and bottom edges of the enclosing box

	fontheight		% Get the height and depth of the current font.

	@startx @starty addcoords
	/@starty xdef /@startx xdef
	@endx @endy addcoords
	/@endy xdef /@endx xdef
	newpath
	@startx @starty moveto
	@endx @starty lineto
	@endx @endy lineto
	@startx @endy lineto
	closepath
        includepath
	grestore

	-awidthshow
	end % $BoundingBox
} def

%
% `image':
%
% Assume here that the image lands in the unit square.
%

/image {
        $BoundingBox begin
        (image called\n) traceprint %%DEBUG
	gsave
	newpath
	0 0 moveto
	1 0 rlineto
	1 1 rlineto
	-1 0 rlineto
	closepath
	includepath
	grestore

	-image
	end % $BoundingBox
} def

/imagemask
{
    $BoundingBox begin
    (imagemask called\n) traceprint %%DEBUG
    gsave
    newpath
    0 0 moveto
    1 0 rlineto
    1 1 rlineto
    -1 0 rlineto
    closepath
    includepath
    grestore

    -imagemask
    end % $BoundingBox
} def

% Just define this one out of existence
/framedevice { pop pop pop pop } def

% Handle restoring VM --- this is all OK, except that we have to
% hang onto the bb info we collected while in the about-to-be-discarded
% environment

/restore
{
    $BoundingBox begin
    (restore called\n) traceprint %%DEBUG
    tracedump  %% HACK, but the only way I see right now to get this stuff!
    bbox-llx bbox-lly bbox-urx bbox-ury
    5 -1 roll
    -restore
    /bbox-ury xstore /bbox-urx xstore
    /bbox-lly xstore /bbox-llx xstore
    end % $BoundingBox
} def


%
% `showpage':
%
% Just draw the box around the figure and print the page, and then initialize
% the bounding box variables again.
%

$BoundingBox begin
/temp-string 10 string def
end % $BoundingBox

/showpage {
	$BoundingBox begin
	initgraphics

        (showpage\n) traceprint % DEBUG
	dump-bbox  % DEBUG

        /bbox-llx round_down
	/bbox-lly round_down
	/bbox-ury round_up
	/bbox-urx round_up

	bbox-llx bbox-lly moveto		% Make the box
	bbox-llx bbox-ury lineto
	bbox-urx bbox-ury lineto
	bbox-urx bbox-lly lineto
	closepath

	bwstroke			% Draw the box.

% Print the size of the bounding box both above and below the actual box
	0 setgray
	/Courier findfont 10 scalefont setfont
	bbox-llx 36 max bbox-lly 12 sub 36 max moveto
	(%%BoundingBox: ) -show
	bbox-llx temp-string cvs -show ( ) -show
	bbox-lly temp-string cvs -show ( ) -show
	bbox-urx temp-string cvs -show ( ) -show
	bbox-ury temp-string cvs -show

	bbox-llx 36 max bbox-ury 12 add 740 min moveto
	(%%BoundingBox: ) -show
	bbox-llx temp-string cvs -show ( ) -show
	bbox-lly temp-string cvs -show ( ) -show
	bbox-urx temp-string cvs -show ( ) -show
	bbox-ury temp-string cvs -show

	init
	-showpage
	tracedump        %% DEBUG
	end % $BoundingBox
} def

%
% BoundingBox functions:
%
% We accumulate the information about the bounding box into four variables.
% The data is stored in default coordinates.
%

$BoundingBox begin

/init {
	/bbox-llx 99999 store
	/bbox-lly 99999 store
	/bbox-urx -99999 store
	/bbox-ury -99999 store
} def

/bbox-llx 0 def
/bbox-lly 0 def
/bbox-urx 0 def
/bbox-ury 0 def

%
% - `includepath' -
%
% Incorporates the bounding box of the path into the bounding box info.
%   ... Gets the bounding box in default coords

/includepath {
        (Adding a path: ) traceprint %%DEBUG
	gsave
        initmatrix
	{
		0 setlinejoin
		flattenpath
	} stopped {
		(Couldn't flatten the path\n) traceprint % DEBUG
		grestore
		gsave
		initmatrix
	} if
	{ pathbbox } stopped not
	{
            4 2 roll    % Just so we get lower-left first
	    2 copy dump-coord %%DEBUG
	    dup bbox-lly lt {
		    /bbox-lly xstore
	    } {
		    pop
	    } ifelse
	    dup bbox-llx lt {
		    /bbox-llx xstore
	    } {
		    pop
	    } ifelse

	    (; ) traceprint 2 copy dump-coord (\n) traceprint %%DEBUG
	    dup bbox-ury gt {
		    /bbox-ury xstore
	    } {
		    pop
	    } ifelse
	    dup bbox-urx gt {
		    /bbox-urx xstore
	    } {
		    pop
	    } ifelse
	    dump-bbox  %%DEBUG
	} if
	grestore
} def

%
% A nice black-and white line drawing function.
%

/bwstroke {
	0 setlinewidth			% Thinnest possible lines
	1 setgray			% White first
	[5] 0 setdash			% Only half the line
	gsave -stroke grestore
	0 setgray			% Then black
	[5] 5 setdash			% On the other half
	-stroke
} def

%
% Stuff for text.
%

%
% char-code string `chcount' occurs
%
% Counts the number of times a character appears in a string.
%

/chcount {
	0 exch
	{
		2 index eq {
			1 add
		} if
	} forall
	exch pop
} def

%
% - `fontheight' heightx heighty depthx depthy
%
% Returns the offsets to the lowest point and highest point in the current
% font.
%

/fontheight {
	currentfont begin
	/FontBBox load aload pop
	exch pop 0 exch
	FontMatrix transform
	4 2 roll
	exch pop 0 exch
	FontMatrix transform
	end
} def

% key round_{down|up} -  These will round the value of the given key
%                         up or down, as appropriate, to the nearest integer
/round_up   { dup load ceiling cvi store } def
/round_down { dup load floor   cvi store } def

% key binddefinition - this will do a 'bind' on the procedure given by 'key'
/binddefinition
{
    dup where
    {
        exch
        2 copy
        get bind put
    }
    { undefined } ifelse
} def

% Given two numbers on the stack, return with just the smallest
/min { 2 copy ge { exch } if pop } def

% Dito for the largest of the pair
/max { 2 copy lt { exch } if pop } def


% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
%
%   Debugging utilities
%

/$tracedict where
{  % Trace package loaded... do the tracing
    pop
% This is a debugging function to print out what is going on.
%  Format <argn> <argn-1> ... <arg1> n <string> debug <argn> ... <arg1>
%    (that is, the 'n' args will be *left* on the stack!)
/debug
{
    traceprint (\n) traceprint
    dup 1 add   % Now total number of args (including arg count)
    copy
    {
        (    ) traceprint
	trace=
        (\n) traceprint
    } repeat
    pop    % Remove the extra copy of the arg count
} def

% Print out a coordinate on the stack:  x y ---
/dump-coord
{
    (\() traceprint exch trace= (, ) traceprint trace= (\)) traceprint
} def

% Print out bb's current notion of its bounding box

/dump-bbox
{
    (Bounding Box: ) traceprint
    bbox-llx bbox-lly dump-coord
    (; ) traceprint
    bbox-urx bbox-ury dump-coord
    (\n) traceprint
} def

tracebegin %% DEBUG

}
{ % No trace package loaded, so don't trace.  Stub out the various calls

/traceprint { pop } def
/dump-coord { pop pop } def
/dump-bbox { } def
/debug { pop  pop } def
/tracedump { } def

} ifelse

% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %

% Bind everything

/xdef binddefinition
/xstore binddefinition
/addcoords binddefinition
/stroke binddefinition
/fill binddefinition
/eofill binddefinition
/show binddefinition
/widthshow binddefinition
/ashow binddefinition
/awidthshow binddefinition
/image binddefinition
/showpage binddefinition
/init binddefinition
/includepath binddefinition
/bwstroke binddefinition
/chcount binddefinition
/fontheight binddefinition

/debug binddefinition
/dump-coord binddefinition
/dump-bbox binddefinition

% Start it up.

init

end % $BoundingBox

%  end of bb.ps

% $Log:	bb.ps,v $
% Revision 1.14  91/03/21  13:04:02  cosell
% Relocated the position of the constrained BBox info
%
% Revision 1.13  91/03/21  12:21:04  cosell
% Forced the %BoundingBox info to stay within the page boundaries
%
% Revision 1.12  91/03/21  12:15:17  cosell
% Added a tracing hook to bridge restores.
%
% Revision 1.11  90/07/02  08:48:40  cosell
% bbfig now correctly copes with empty paths
%
% Revision 1.10  90/06/27  10:47:22  cosell
% Added a bunch of improvements from Joe Pallas at stanford.
%
% Revision 1.9  90/06/26  10:50:20  cosell
% Stack got botched in the 'debug' stub
%
% Revision 1.8  90/06/25  09:34:51  cosell
% Minor bug in 'restore'
%
% Revision 1.7  90/06/25  09:29:58  cosell
% Added code to catch and deal with 'restore'.  Thanks to Frank
% Jensen for finding this one
%
% Revision 1.6  90/06/25  09:23:26  cosell
% Small bugfix in the text-handling stuff
%
% Revision 1.5  90/06/10  09:04:02  cosell
% Changed the printed string to explictly say "%%BoundingBox"
%
% Revision 1.4  90/06/10  08:55:39  cosell
% Added 'bind' machinery to insulate this package from later redefinitions
% of things we need from the systemdict.
%
% Revision 1.3  90/06/10  08:28:53  cosell
% Added debugging hooks.  They don't affect anything (and don't do
% anything) in the normal use of bbfig.  But if the 'trace' package
% is loaded ahead of this, it'll print out some helpful info.  Probably
% I'll end up removing all of this if/when I really get the package
% up to snuff.
%
% Revision 1.2  90/05/25  12:08:24  cosell
% Major improvements and tuneups:  fixed it to really use its private
% discionary, and the most importnat: it now computes the bounding box
% in *default* coords
%
% Revision 1.1  90/05/23  08:18:54  cosell
% Initial revision
%   This is Ned Bachelder's original version
